﻿//Re-Used follwoing snippet for distance calculation
//http://www.codeproject.com/KB/mobile/WiMoPower1.aspx

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Maps.MapControl;

namespace TrailMap.Utils
{
    public static class GeoCodeCalc
    {
        public const double EarthRadiusInMiles = 3956.0;
        public const double EarthRadiusInKilometers = 6367.0;
        public static double ToRadian(double val) { return val * (Math.PI / 180); }
        public static double ToDegree(double val) { return val * 180 / Math.PI; }
        public static double DiffRadian(double val1, double val2) { return ToRadian(val2) - ToRadian(val1); }

        /// <summary> 
        /// Calculate the distance between two geocodes. Defaults to using Miles. 
        /// </summary> 
        public static double CalcDistanceInMiles(double lat1, double lng1, double lat2, double lng2)
        {
            return CalcDistance(lat1, lng1, lat2, lng2, EarthRadiusInMiles);
        }

        /// <summary> 
        /// Calculate the distance between two geocodes. Defaults to using Miles. 
        /// </summary> 
        public static double CalcDistanceInKilometers(double lat1, double lng1, double lat2, double lng2)
        {
            return CalcDistance(lat1, lng1, lat2, lng2, EarthRadiusInKilometers);
        }

        /// <summary> 
        /// Calculate the distance between two geocodes. 
        /// </summary> 
        public static double CalcDistance(double lat1, double lng1, double lat2, double lng2, double radius)
        {

            return radius * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(lat1, lat2)) / 2.0), 2.0) + Math.Cos(ToRadian(lat1)) * Math.Cos(ToRadian(lat2)) * Math.Pow(Math.Sin((DiffRadian(lng1, lng2)) / 2.0), 2.0)))));
        }

        public static Double Bearing(double lat1, double lng1, double lat2, double lng2)
        {

            {
                var dLat = lat2 - lat2;
                var dLon = lng2 - lng1;
                var dPhi = Math.Log(Math.Tan(lat2 / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4));
                var q = (Math.Abs(dLat) > 0) ? dLat / dPhi : Math.Cos(lat1);

                if (Math.Abs(dLon) > Math.PI)
                {
                    dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon);
                }
                //var d = Math.Sqrt(dLat * dLat + q * q * dLon * dLon) * R;
                var brng = ToDegree(Math.Atan2(dLon, dPhi));
                return brng;
            }

        }

        //public static double ToRadian(double degrees)
        //{
        //    return degrees * (Math.PI / 180);
        //}

        //public static double ToDegrees(double radians)
        //{
        //    return radians * (180 / Math.PI);
        //}

        public static LocationCollection CreateCircle(Location center, double radius, DistanceMeasure units)
        {
            var earthRadius = (units == DistanceMeasure.Miles ? GeoCodeCalc.EarthRadiusInMiles : GeoCodeCalc.EarthRadiusInKilometers);
            var lat = ToRadian(center.Latitude); //radians
            var lng = ToRadian(center.Longitude); //radians
            var d = radius / earthRadius; // d = angular distance covered on earth's surface
            var locations = new LocationCollection();

            for (var x = 0; x <= 360; x++)
            {
                var brng = ToRadian(x);
                var latRadians = Math.Asin(Math.Sin(lat) * Math.Cos(d) + Math.Cos(lat) * Math.Sin(d) * Math.Cos(brng));
                var lngRadians = lng + Math.Atan2(Math.Sin(brng) * Math.Sin(d) * Math.Cos(lat), Math.Cos(d) - Math.Sin(lat) * Math.Sin(latRadians));

                locations.Add(new Location(ToDegree(latRadians), ToDegree(lngRadians)));
            }

            return locations;
        }

    }

    public enum DistanceMeasure
    {
        Miles,
        Kilometers
    }
}
